#include "do_mqtt.h"
#include "bsp_tim.h"
#include "cJSON.h"
#include "delay.h"
#include "do_dns.h"
#include "wiz_interface.h"
#include "wizchip_conf.h"
#include "ws2812b.h"
#include <stdio.h>
#include <string.h>

#define MQTT_ETHERNET_MAX_SIZE (1024 * 2)
#define MQTT_RECV_MSG_MAX_SIZE 512
#define STATUS_MSG_MAX_SIZE    256

/* Definition of light mode variables */
uint8_t g_flowing_light = 0;
uint8_t g_rainbow_effect = 0;

/* Definition of color variables */
uint8_t g_red_value = 0;
uint8_t g_green_value = 0;
uint8_t g_blue_value = 0;

static MQTTClient s_mqtt_client = {0};
static Network s_network = {0};
static uint8_t s_run_status = CONN;

static mqtt_conn_t g_mqtt_params = {
    .mqtt_host_url = "mqtts.heclouds.com",
    .server_ip = {0},
    .port = 1883,
    .client_id = "d2",
    .username = "iP20B5FpF6",
    .password = "version=2018-10-31&res=products%2FiP20B5FpF6%2Fdevices%2Fd2&",
    .pub_topic = "$sys/iP20B5FpF6/d2/thing/property/post",
    .pub_topic_reply = "$sys/iP20B5FpF6/d2/thing/property/post/reply",
    .sub_topic = "$sys/iP20B5FpF6/d2/thing/property/set",
    .sub_topic_reply = "$sys/iP20B5FpF6/d2/thing/property/set_reply",
    .pub_qos = QOS0,
    .will_topic = "/wizchip/will",
    .will_qos = QOS0,
    .will_msg = "wizchip offline!",
    .sub_qos = QOS0,

};

static MQTTMessage s_pub_message = {
    .qos = QOS0,
    .retained = 0,
    .dup = 0,
    .id = 0,
};

static char s_mqtt_recv_msg[MQTT_RECV_MSG_MAX_SIZE] = {0};
static uint8_t s_mqtt_recv_flag = 0;

static MQTTPacket_willOptions s_will_data = MQTTPacket_willOptions_initializer;
static MQTTPacket_connectData s_connect_data =
    MQTTPacket_connectData_initializer;

/**
 * @brief Initialize MQTT client
 *
 * @param sn Socket number
 * @param send_buf_ptr Send buffer pointer
 * @param recv_buf_ptr Receive buffer pointer
 *
 * @note Performs DNS resolution and establishes network connection
 */
void mqtt_init(uint8_t sn, uint8_t *send_buf_ptr, uint8_t *recv_buf_ptr)
{
    wiz_NetInfo net_info = {0};
    wizchip_getnetinfo(&net_info);

    /* DNS resolution */
    if (do_dns(send_buf_ptr, (uint8_t *)g_mqtt_params.mqtt_host_url, g_mqtt_params.server_ip))
    {
        while (1)
        {
            /* Handle DNS failure */
        }
    }

    NewNetwork(&s_network, sn);
    ConnectNetwork(&s_network, g_mqtt_params.server_ip, g_mqtt_params.port);

    MQTTClientInit(&s_mqtt_client, &s_network, 1000, send_buf_ptr, MQTT_ETHERNET_MAX_SIZE, recv_buf_ptr, MQTT_ETHERNET_MAX_SIZE);

    s_will_data.qos = g_mqtt_params.will_qos;
    s_will_data.topicName.lenstring.data = g_mqtt_params.will_topic;
    s_will_data.topicName.lenstring.len =
        strlen(s_will_data.topicName.lenstring.data);
    s_will_data.message.lenstring.data = g_mqtt_params.will_msg;
    s_will_data.message.lenstring.len =
        strlen(s_will_data.message.lenstring.data);
    s_will_data.retained = 0;
    s_will_data.struct_version = 3;

    s_connect_data.will = s_will_data;
    s_connect_data.MQTTVersion = 4;
    s_connect_data.clientID.cstring = g_mqtt_params.client_id;
    s_connect_data.username.cstring = g_mqtt_params.username;
    s_connect_data.password.cstring = g_mqtt_params.password;
    s_connect_data.keepAliveInterval = 30;
    s_connect_data.cleansession = 1;
}

/**
 * @brief Handle color update command
 *
 * @param color_value Color value to set
 * @param color_type Color type (RED/GREEN/BLUE)
 * @param status_msg Status message buffer
 */
static void handle_color_command(int color_value, const char *color_type, char *status_msg)
{
    char temp[16] = {0};
    snprintf(temp, sizeof(temp), "%s:%d;", color_type, color_value);
    strncat(status_msg, temp, STATUS_MSG_MAX_SIZE - strlen(status_msg) - 1);

    if (strcmp(color_type, "RED") == 0)
    {
        g_red_value = color_value;
    }
    else if (strcmp(color_type, "GREEN") == 0)
    {
        g_green_value = color_value;
    }
    else if (strcmp(color_type, "BLUE") == 0)
    {
        g_blue_value = color_value;
    }

    for (uint8_t i = 0; i < LED_NUM; i++)
    {
        ws2812b_set_color(i, g_red_value, g_green_value, g_blue_value);
    }
    ws2812b_update();
}

/**
 * @brief Parse and process MQTT message
 *
 * @param msg JSON message to parse
 */
void json_decode(char *msg)
{
    int ret = 0;
    char status_msg[STATUS_MSG_MAX_SIZE] = {0};
    cJSON *json_data = cJSON_Parse(msg);

    if (json_data == NULL)
    {
        printf("JSON parse fail.\r\n");
        return;
    }

    cJSON *id = cJSON_GetObjectItem(json_data, "id");
    cJSON *params = cJSON_GetObjectItem(json_data, "params");

    if (params == NULL || params->type != cJSON_Object)
    {
        printf("Invalid params format!\r\n");
        cJSON_Delete(json_data);
        return;
    }

    /* Process LightStrip command */
    cJSON *light_strip = cJSON_GetObjectItem(params, "LightStrip");
    if (light_strip != NULL)
    {
        if (light_strip->valueint)
        {
            strncat(status_msg, "LightStrip:OK;", sizeof(status_msg) - strlen(status_msg) - 1);

            g_rainbow_effect = 1;
        }
        else
        {
            strncat(status_msg, "LightStrip:OFF;", sizeof(status_msg) - strlen(status_msg) - 1);
            g_rainbow_effect = 0;
            ws2812b_clear_all();
            ws2812b_update();
        }
    }

    /* Process FlowingLight command */
    cJSON *flowing_light = cJSON_GetObjectItem(params, "FlowingLight");
    if (flowing_light != NULL)
    {
        if (flowing_light->valueint)
        {
            printf("Switch on the chasing lights\r\n");
            g_flowing_light = 1;
        }
        else
        {
            printf("Switch off the chasing lights\r\n");
            g_flowing_light = 0;
            ws2812b_clear_all();
            ws2812b_update();
        }
    }

    /* Process color commands */
    cJSON *red = cJSON_GetObjectItem(params, "red");
    if (red != NULL)
    {
        handle_color_command(red->valueint, "RED", status_msg);
    }

    cJSON *green = cJSON_GetObjectItem(params, "green");
    if (green != NULL)
    {
        handle_color_command(green->valueint, "GREEN", status_msg);
    }

    cJSON *blue = cJSON_GetObjectItem(params, "blue");
    if (blue != NULL)
    {
        handle_color_command(blue->valueint, "BLUE", status_msg);
    }

    /* Construct and send reply */
    char reply_msg[STATUS_MSG_MAX_SIZE * 2] = {0};
    const char *id_str = (id != NULL) ? id->valuestring : "UNKNOWN";

    snprintf(reply_msg, sizeof(reply_msg), "{\"id\":\"%s\",\"code\":200,\"msg\":\"success\",\"status\":\"%s\"}", id_str, status_msg);

    printf("reply:%s\r\n", reply_msg);

    s_pub_message.payload = reply_msg;
    s_pub_message.payloadlen = strlen(reply_msg);

    ret = MQTTPublish(&s_mqtt_client, g_mqtt_params.sub_topic_reply, &s_pub_message);
    if (ret != SUCCESSS)
    {
        s_run_status = ERR;
        printf("Publish failed! Error: %d\r\n", ret);
    }
    else
    {
        printf("Published: %s\n", (char *)s_pub_message.payload);
    }

    cJSON_Delete(json_data);
}

/**
 * @brief MQTT message arrival callback
 *
 * @param msg_data Message data structure
 */
void message_arrived(MessageData *msg_data)
{
    char topic_name[64] = {0};
    char msg_content[512] = {0};

    snprintf(topic_name, sizeof(topic_name), "%.*s", (int)msg_data->topicName->lenstring.len, msg_data->topicName->lenstring.data);

    snprintf(msg_content, sizeof(msg_content), "%.*s", (int)msg_data->message->payloadlen, (char *)msg_data->message->payload);

    printf("recv:%s,%s\r\n\r\n", topic_name, msg_content);

    if (strcmp(topic_name, g_mqtt_params.sub_topic) == 0)
    {
        s_mqtt_recv_flag = 1;
        memset(s_mqtt_recv_msg, 0, sizeof(s_mqtt_recv_msg));
        strncpy(s_mqtt_recv_msg, msg_content, sizeof(s_mqtt_recv_msg) - 1);
    }
}

/**
 * @brief Connect to MQTT broker
 *
 * @return int Connection status (SUCCESSS or error)
 */
static int connect_to_broker(void)
{
    int ret = MQTTConnect(&s_mqtt_client, &s_connect_data);
    printf("Connect to MQTT server: %d.%d.%d.%d:%d\r\n",
           g_mqtt_params.server_ip[0],
           g_mqtt_params.server_ip[1],
           g_mqtt_params.server_ip[2],
           g_mqtt_params.server_ip[3],
           g_mqtt_params.port);

    printf("Connected:%s\r\n\r\n", ret == SUCCESSS ? "success" : "failed");
    return ret;
}

/**
 * @brief Subscribe to MQTT topics
 *
 * @return int Subscription status (SUCCESSS or error)
 */
static int subscribe_to_topics(void)
{
    int ret = MQTTSubscribe(&s_mqtt_client, g_mqtt_params.sub_topic, g_mqtt_params.sub_qos, message_arrived);
    printf("Subscribing to %s\r\n", g_mqtt_params.sub_topic);
    printf("Subscribed:%s\r\n\r\n", ret == SUCCESSS ? "success" : "failed");

    if (ret != SUCCESSS)
    {
        return ret;
    }

    ret = MQTTSubscribe(&s_mqtt_client, g_mqtt_params.pub_topic_reply, g_mqtt_params.sub_qos, message_arrived);
    printf("Subscribing to %s\r\n", g_mqtt_params.pub_topic_reply);
    printf("Subscribed:%s\r\n\r\n", ret == SUCCESSS ? "success" : "failed");

    return ret;
}

/**
 * @brief Perform MQTT operations based on current state
 */
void do_mqtt(void)
{
    int ret = SUCCESSS;

    switch (s_run_status)
    {
    case CONN:
        ret = connect_to_broker();
        s_run_status = (ret == SUCCESSS) ? SUB : ERR;
        break;

    case SUB:
        ret = subscribe_to_topics();
        s_run_status = (ret == SUCCESSS) ? PUB_MESSAGE : ERR;
        break;

    case PUB_MESSAGE:
        s_run_status = KEEPALIVE;
        break;

    case KEEPALIVE:
        if (MQTTYield(&s_mqtt_client, 30) != SUCCESSS)
        {
            s_run_status = ERR;
        }
        /* Fall through to RECV state */

    case RECV:
        if (s_mqtt_recv_flag)
        {
            s_mqtt_recv_flag = 0;
            json_decode(s_mqtt_recv_msg);
        }
        s_run_status = KEEPALIVE; // Return to keepalive after processing
        break;

    case ERR:
        printf("System ERROR!");
        break;

    default:
        /* Handle unexpected state */
        s_run_status = CONN;
        break;
    }
}
